<?php
namespace adapters;

class db extends \patterns\Adapter /*implements \interfaces\db */
/* implements \interfaces\db*/
{
	//protected $lib;

	protected $queryType = "";

	protected $distinct = false;

	protected $columns = array();
	protected $from = array();
	protected $where = array();

	protected $join = array();

	protected $having = array();

	protected $random = false;

	protected $fetch = "";

	protected $orderBy = array();
	protected $orderByDirection = "";

	protected $groupBy = array();
	protected $groupByDirection = "";

	protected $limit = array();

	private $sql = "";
	private $queries = array();

	public $placeholders = array();

	/*Basic actions*/
	public function Select()
	{
		$this->Reset();
		$args = func_get_args();
		if (empty($args))
		{
			$args = array("*");
		}

		$this->queryType = "select";
		$this->columns = $args;

		return $this;
	}

	public function Delete()
	{
		$this->Reset();
		$this->queryType = "delete";
	}

	public function Update()
	{
		$this->Reset();
		$this->queryType = "update";
		$this->columns = func_get_args();
	}

	public function Query()
	{
		$this->Reset();
		$this->queryType = "query";
	}

	public function Join($table, $type = "INNER")
	{
		$this->join[$table] = array("type"=>$type, "ons"=>array());
		return $this;
	}

	public function On($col1, $sign, $col2, $rel = "AND")
	{
		$arr = "";
		$arr = &$this->join[end(array_keys($this->join))];

		$arr['ons'][] = array("col1"=>$col1, "sign"=>$sign, "col2"=>$col2, "rel"=>$rel);

		return $this;
	}

	public function Run($method = "")
	{
		$method = "Do".ucfirst($this->queryType);

		$sql = $this->GetSql();
		$md5d = md5($sql);

		if (isset($this->queries[$md5d]))
		{
			return $this->queries[$md5d]['result'];
		}

		service("debug")->TimerStart();
		$res = $this->$method();
		$end = service("debug")->TimerStop();

		$this->queries[$md5d] = array("sql"=>$sql, "time"=>$end, "result"=>$res);

		return $res;
	}

	public function Distinct($distinct = true)
	{
		$this->distinct = (boolean)$distinct;
		return $this;
	}

	public function GroupBy()
	{
		$args = func_get_args();
		$dir = array_pop($args);

		if ($dir !== "asc" && $dir !== "desc")
		{
			$args[] = $dir;
		}

		$this->groupBy = $args;
		$this->groupDirection = $dir;

		return $this;
	}

	public function Having()
	{
		$args = func_get_args();
		$this->having = $args;
		return $this;
	}

	public function OrderBy()
	{
		$args = func_get_args();
		$dir = array_pop($args);

		if (!preg_match("~desc|asc~iu", $dir))
		{
			$args[] = $dir;
			$dir = "";
		}

		$this->orderBy = $args;
		$this->orderByDirection = $dir;

		return $this;
	}

	public function Limit()
	{
		$args = func_get_args();

		if (count($args) == 2)
		{
			$this->limit = array_reverse($args);
		}
		else
		{
			$this->limit = $args;
		}

		return $this;
	}

	public function Random()
	{
		$this->orderBy = array("RAND()");
		return $this;
	}

	public function Fetch($return)
	{
		$this->fetch = $return;
		return $this;
	}

	////////////////

	public function From()
	{
		$this->from = func_get_args();
		return $this;
	}

	public function Where()
	{
		$this->where[] = func_get_args();
		return $this;
	}

	public function Reset()
	{
		$args = func_get_args();

		$allowed = array("fetch", "distinct", "columns", "from", "where", "having", "orderBy", "orderByDirection", "groupBy", "groupByDirection", "limit", "join");

		if (empty($args))
		{
			$vars = array_keys(get_object_vars($this));
			$vars = array_intersect($allowed, $vars);
		}
		else
		{
			$vars = func_get_args();
			$vars = array_intersect($allowed, $vars);
		}

		foreach ($vars as $item)
		{
			if (is_array($this->$item))
			{
				$this->$item = array();
			}
			else
			{
				$this->$item = "";
			}
		}

		return $this;
	}


	//////////////


	public function GetSql()
	{
		$method = "Get".$this->queryType;

		$sql = $this->$method();

		return $sql;
	}

	function GetSelect()
	{
		$sql = "SELECT\n\t";

		!empty($this->distinct) AND $sql .= "DISTINCT\n\t";

		$sql .= implode(",\n\t", $this->columns);

		!empty($this->from) AND $sql .= "\n\rFROM\n\t" . implode(",\n\t", $this->from);

		//Join
		$joins = "";

		foreach ($this->join as $key=>$item)
		{
			$join = "\r$item[type] JOIN\n\t$key\n\t ON";

			foreach ($item['ons'] as $key=>$on)
			{
				$str = "\n\t" . $on['col1'] . " " . $on['sign'] . " " . $on['col2']."\n";

				if ($key > 0)
				{
					$str = $on['rel'] . $str;
				}

				$join .= $str;
			}

			$joins .= "\r\n".$join."\n";
		}

		!empty($joins) AND $sql .= $joins;



		//Where
		$where = array();

		foreach ($this->where as $item)
		{
			if (count($item) == 3)
			{
				$item[] = "AND";
			}

			$action = array_pop($item);
			$item = implode(" ", $item);

			if (!isset($first))
			{
				$first = true;
			}
			else
			{
				$where[] = $action;
			}

			$where[] = $item;
		}

		!empty($where) AND $sql .= "\nWHERE\n\t" . implode("\n\t", $where);

		!empty($this->groupBy) AND $sql .= "\nGROUP BY\n\t" . implode(",", $this->groupBy);
		!empty($this->groupByDirection) AND $sql .= $this->groupByDirection;

		!empty($this->having) AND $sql .= "\nHAVING\n\t" . implode(",", $this->having);

		!empty($this->orderBy) AND $sql .= "\nORDER BY\n\t" . implode(",", $this->orderBy);

		!empty($this->orderByDirection) AND $sql .= "\n\t".$this->orderByDirection;

		!empty($this->limit) AND $sql .= "\nLIMIT\n\t" . implode(",", $this->limit);

		$sql = $this->SetPlaceholders($sql);

		return $sql;
	}

	private function SetPlaceholders($sql)
	{
		if ($this->usesPlaceholders === true)
		{
			$sql = preg_replace(array("~\?#~u", "~\?~u"), array($this->phTable, $this->phValue), $sql);
		}

		return $sql;
	}

	public function Placeholders()
	{
		$this->placeholders = func_get_args();

		return $this;
	}

	/**
	 * Returns all sql-queries
	 *
	 * @return integer
	 */
	public function GetQueries()
	{
		return $this->queries;
	}
}